Skip to content

feat(mobile): app badge tracks glanceable needs-input count - #5873

Merged
iscekic merged 21 commits into
mainfrom
kwf/deliver-the-work-described-b-fbf8
Sep 7, 2026
Merged

feat(mobile): app badge tracks glanceable needs-input count#5873
iscekic merged 21 commits into
mainfrom
kwf/deliver-the-work-described-b-fbf8

Conversation

@iscekic

@iscekic iscekic commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Changelog for users

  • The launcher app badge now shows the glanceable "needs input" count — the number of active sessions waiting for your input — instead of the unread chat message count.
  • The badge follows the needs-input count whenever it changes: in the foreground, on background refresh, and on push, and it clears when the count reaches zero.
  • Unread chat messages no longer move the launcher badge.

Changelog for maintainers

  • The OS badge is no longer written from the mark-read or unread-counts query paths; those now only maintain the badge cache. Removed apps/mobile/src/lib/badge-freshness.ts and apps/mobile/src/lib/badge-hydration.ts.
  • Glanceable pushes now carry badge: snapshot.needsInput (iOS priority: high; Android priority: default, sound: null, channelId: 'active-agents'), collapsed per scope key via collapseId instead of tag.
  • Chat message pushes no longer include a badge total; NotificationChannelDO.incrementBucket now returns void (bucket accumulation still feeds the unread API).
  • The foreground notification handler now returns shouldSetBadge from whether the glanceable push data was applied and refreshes active sessions from a successfully applied push via the new refreshActiveSessionsFromPush.
  • useCurrentUserId keeps the error state visible during a retry (isError || (isLoading && isFetched)).

Merge with main

Main's idle-window design (#5881) landed while this branch was open. It keeps a Live Activity alive
while every agent is idle and never raises one from idle; this branch had made idle end the card at
once. The merge keeps main's rule at all four sites that carried the older one: app-shared
(buildGlanceableSnapshot, isEligibleGlanceableWork), glanceable-delivery.ts,
glanceable-refresh.ts, and the mobile widget layout with view-props.ts.

The branch now changes no Live Activity behaviour. Its badge work is unaffected: the badge reads
snapshot.needsInput directly. The three tests that asserted the reverted rule are gone (the idle
cases in publisher.test.ts and ios-sink.test.ts, and active-agents-live-activity.test.ts).

Every E2E result below about the launcher badge still holds. The two lines that also asserted the
card disappearing on idle are marked above.

Changes by area

Mobile — mark-read / unread-counts paths (kilo-chat/hooks/mark-read-operation.ts, kilo-chat/hooks/use-mark-read.ts, lib/hooks/use-unread-counts.ts, lib/hooks/use-unread-counts-invalidation.ts): removed all OS badge writes and the badge-freshness epoch from these paths so chat reads only maintain the cache and never touch the launcher badge; deleted the now-unused badge-freshness.ts and badge-hydration.ts.

Mobile — glanceable publisher (lib/glanceable/publisher.ts): a change in needsInput now cancels coalescing and emits immediately, so badge changes reach the sinks without the happy-path coalesce delay.

Mobile — app badge sink (lib/notifications.ts): added a glanceable sink that writes the OS badge from snapshot.needsInput through a serialized, count-change-guarded write (avoiding clobbering a badge iOS applied from a visible push); it applies foreground push badges, waits for a pending badge write before a background task finishes, and clears on terminal snapshots.

Mobile — active sessions live sync (lib/active-sessions-live-sync.ts): exposed refreshActiveSessionsFromPush so a successfully applied glanceable push triggers a manual refresh.

Mobile — current user hook (lib/hooks/use-current-user-id.ts): keeps the error state visible while a failed request retries.

Notifications service — chat push delivery (dos/NotificationChannelDO.ts): stopped attaching a badge total to chat message pushes; bucket math still accumulates for the unread API.

Notifications service — glanceable delivery (lib/glanceable-delivery.ts): buildGlanceableExpoMessages now takes a platform and attaches badge: snapshot.needsInput to the glanceable push, keeping the badge and snapshot ordered together.

E2E proof

Proved live on iOS and Android simulators (backend and mobile-app sections passed; mobile: typecheck + unit + i18n + unused gates and the mobile-device verifier passed, spot check clean).

Badge follows the needs-input count:

  • Happy push / Happy foreground: one question session active → launcher badge 1 and in-app Agents 1.
  • Edge count: two questions and one busy session → launcher badge 2. — Launcher badge 2 after two question + one busy (e6-badge.png); rec e6-ios-trim.mp4.
  • Edge counts: publish 0, 1, and 12 → launcher shows clear, 1, and 12. — Launcher clear, 1, and 12 (e20-ios-count-0.png, e20-ios-count-1.png, e20-ios-count-12.png); rec e20-ios-trimmed.mp4.
  • Empty push: zero needs-input sessions → launcher badge clears. — Launcher badge 0 (e2-badge.png) equals in-app Agents 0 (e2-inapp.png, e2-agents.png); rec e2-ios-trim.mp4.
  • Empty state: finish every active session → launcher badge remains absent. — After idle, launcher badge absent (e10-ios-after-badge.png, e10-ios-home.png); rec e10-ios-trimmed.mp4. The recording also shows the Live Activity gone, which no longer applies: see "Merge with main" below.
  • Explicit no-input proof: change the question session to busy → launcher badge clears.
  • Permission state: one permission and one busy session → launcher badge 1.
  • Foreground push: deliver a glanceable push with count 2 while open → launcher badge 2. — iOS: while open, glanceable push count 2 set launcher badge 2 (e1-ios-trim.mp4, e1-ios-home-before.png, e1-ios-fg-banner.png, e1-ios-badge.png); in-app Agents stayed 1 LIVE with no needs-input count (e1-ios-inapp.png).

Badge does not track unread chat:

  • Ordinary push / Ordinary push isolation: unread chat pushes do not change the needs-input badge. — Ordinary chat push badge 9 while backgrounded did not set unread 9 (e3-ios-before-badge.png, e3-ios-after-ordinary-push-badge.png); rec e3-ios-ordinary-push.mp4.

Lifecycle and robustness:

  • Background refresh: background the app, deliver counts 1 then 0 → badge changes then clears.
  • Killed push: kill the app, deliver a glanceable count → headless path updates the badge. — Killed app: no badge (e18-ios-before-killed.png), then headless push sets badge 1; in-app Agents 1 (e18-ios-inapp-count-1.png, e18-ios-trimmed.mp4).
  • Privacy state: switch from an organization with badge 1 → the old organization count clears. — Org badge 1 clears after switch to Personal (e14-ios-personal-badge-cleared.png, e14-ios-trimmed.mp4).
  • Non-retryable unhappy: sign out after badge 1 → launcher badge clears immediately.
  • Reconnecting: disconnect with badge 1, change server state, reconnect → refreshed badge matches the new count.
  • Retryable failure / Retryable unhappy: fail one glanceable delivery → prior badge remains until the successful retry replaces it.
  • Unauthorized scope: inaccessible organization refresh → no badge from that scope.
  • Loading state: cold start without a cached snapshot and a delayed first query → no stale badge.
  • Layout stability: loading, content, stale, retry, and empty transitions → no existing surface blanks or moves.
  • ux-check: Android numeric badges — background push shows 1 then clears.
  • ux-check: iOS Live Activity — background the app and create one needs-input session → both surfaces show 1.
  • ux-check: offline queue — queue counts 1 and 0 while offline → after reconnection the final badge remains clear.
  • ux-check: resolve without opening — badge clears. — Resolve without opening: island 1 and badge 1 became dismissed and clear (e11-ios-before-resolve.png, e11-ios.mp4). The card dismissal in that recording no longer applies: see "Merge with main" below.

[e1] [iOS and Android] Happy push: Deliver a glanceable snapshot with one question session; the launcher badge shows 1. — Launcher badge none to 1; in-app 1 LIVE (e1-ios-agents-count-1.png, e1-ios.mp4).

e1-ios.mp4.trim.mp4

[e2] [iOS and Android] Empty push: Deliver a glanceable snapshot with zero needs-input sessions; the launcher badge clears. — Launcher badge 0 (e2-badge.png) equals in-app Agents 0 (e2-inapp.png, e2-agents.png); rec e2-ios-trim.mp4.

e2-ios-trim.mp4.trim.mp4

[e3] [iOS and Android] Ordinary push: Deliver a chat message while backgrounded; the launcher badge does not change to the unread total. — Ordinary chat push badge 9 while backgrounded did not set unread 9 (e3-ios-before-badge.png, e3-ios-after-ordinary-push-badge.png); rec e3-ios-ordinary-push.mp4.

e3-ios-ordinary-push.mp4.trim.mp4

Recording of the verified flow (waits trimmed)

e2-ios-ordinary-push-bg-trim.mp4.trim.mp4

[e2] [iOS and Android] Empty push: Deliver a glanceable snapshot with zero needs-input sessions; the launcher badge clears. — iOS launcher badge cleared to 0 after empty snapshot (e2-ios-trimmed.mp4, e2-ios-before-badge.png, e2-ios-badge-cleared.png, e2-ios-inapp.png).

e2-ios-trimmed.mp4.trim.mp4

[e1] [iOS and Android] Foreground push: Deliver a glanceable push with count 2 while open; the launcher badge becomes 2. — iOS: while open, glanceable push count 2 set launcher badge 2 (e1-ios-trim.mp4, e1-ios-home-before.png, e1-ios-fg-banner.png, e1-ios-badge.png); in-app Agents stayed 1 LIVE with no needs-input count (e1-ios-inapp.png). UX-PREEXISTING: [pre-existing] Dynamic Island 1 while launcher clear (e1-ios-home

e1-ios-trim.mp4.trim.mp4

Pre-existing UX defects observed (not changed here)

  • 01-login-request-code-FAIL.png — Login failure shows a blank white screen with no error message or next action.
  • 01-login-request-code.png — A default-looking blue Refreshing banner overlaps the Kilo logo on the sign-in screen.
  • e10-profile.png — The last Linked Accounts row is clipped under the tab bar so the Test Account email is unreadable.
  • Dynamic Island still shows 1 after launcher clear. After the merge with main this is the intended idle-window behaviour, not a defect.

@kilo-code-bot

kilo-code-bot Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental review of the main merge restoring idle-window Live Activity eligibility and the publisher needs-input coalesce tests; no issues found.

Files Reviewed (11 files)
  • apps/mobile/src/glanceable-ios/active-agents-live-activity.tsx
  • apps/mobile/src/glanceable-ios/ios-sink.test.ts
  • apps/mobile/src/glanceable-ios/ios-sink.ts
  • apps/mobile/src/glanceable-ios/view-props.ts
  • apps/mobile/src/lib/glanceable/presentation.ts
  • apps/mobile/src/lib/glanceable/publisher.test.ts
  • packages/app-shared/src/glanceable-agents-snapshot.test.ts
  • packages/app-shared/src/glanceable-agents-snapshot.ts
  • services/notifications/src/lib/glanceable-delivery.test.ts
  • services/notifications/src/lib/glanceable-delivery.ts
  • services/notifications/src/lib/glanceable-refresh.ts
Previous Review Summaries (4 snapshots, latest commit 2bec1c9)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 2bec1c9)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental review of idle-only glanceable eligibility, Live Activity/widget count gating, and the launcher badge write guard; no issues found.

Files Reviewed (13 files)
  • apps/mobile/src/glanceable-ios/active-agents-live-activity.test.ts
  • apps/mobile/src/glanceable-ios/active-agents-live-activity.tsx
  • apps/mobile/src/glanceable-ios/ios-sink.test.ts
  • apps/mobile/src/glanceable-ios/view-props.ts
  • apps/mobile/src/lib/glanceable/presentation.ts
  • apps/mobile/src/lib/glanceable/publisher.test.ts
  • apps/mobile/src/lib/notifications.test.ts
  • apps/mobile/src/lib/notifications.ts
  • packages/app-shared/src/glanceable-agents-snapshot.test.ts
  • packages/app-shared/src/glanceable-agents-snapshot.ts
  • services/notifications/src/lib/glanceable-delivery.test.ts
  • services/notifications/src/lib/glanceable-delivery.ts
  • services/notifications/src/lib/glanceable-refresh.ts

Previous review (commit c2ffa8c)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental review of .kwf-keep-stack deletion only; no code changes since the prior pass, and no issues found.

Files Reviewed (1 file)
  • .kwf-keep-stack

Previous review (commit c175a89)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (18 files)
  • apps/mobile/src/components/kilo-chat/hooks/mark-read-operation.ts
  • apps/mobile/src/components/kilo-chat/hooks/use-mark-read.ts
  • apps/mobile/src/components/kilo-chat/mark-read-state.test.ts
  • apps/mobile/src/lib/active-sessions-live-sync.ts
  • apps/mobile/src/lib/badge-freshness.ts
  • apps/mobile/src/lib/badge-hydration.ts
  • apps/mobile/src/lib/glanceable/publisher.test.ts
  • apps/mobile/src/lib/glanceable/publisher.ts
  • apps/mobile/src/lib/hooks/use-current-user-id.test.ts
  • apps/mobile/src/lib/hooks/use-current-user-id.ts
  • apps/mobile/src/lib/hooks/use-unread-counts-invalidation.ts
  • apps/mobile/src/lib/hooks/use-unread-counts.ts
  • apps/mobile/src/lib/notifications.test.ts
  • apps/mobile/src/lib/notifications.ts
  • services/notifications/src/__tests__/dispatch-push.test.ts
  • services/notifications/src/dos/NotificationChannelDO.ts
  • services/notifications/src/lib/glanceable-delivery.test.ts
  • services/notifications/src/lib/glanceable-delivery.ts

Previous review (commit edf03a8)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (13 files)
  • apps/mobile/src/components/kilo-chat/hooks/mark-read-operation.ts
  • apps/mobile/src/components/kilo-chat/hooks/use-mark-read.ts
  • apps/mobile/src/components/kilo-chat/mark-read-state.test.ts
  • apps/mobile/src/lib/badge-freshness.ts
  • apps/mobile/src/lib/badge-hydration.ts
  • apps/mobile/src/lib/hooks/use-unread-counts-invalidation.ts
  • apps/mobile/src/lib/hooks/use-unread-counts.ts
  • apps/mobile/src/lib/notifications.test.ts
  • apps/mobile/src/lib/notifications.ts
  • services/notifications/src/__tests__/dispatch-push.test.ts
  • services/notifications/src/dos/NotificationChannelDO.ts
  • services/notifications/src/lib/glanceable-delivery.test.ts
  • services/notifications/src/lib/glanceable-delivery.ts

Reviewed by grok-4.6 · Input: 178.2K · Output: 14.6K · Cached: 455.6K

Review guidance: REVIEW.md from base branch main

@iscekic iscekic added the human-ready The PR is ready for human review. label Sep 4, 2026
@iscekic
iscekic requested a review from eshurakov September 4, 2026 11:06
@iscekic
iscekic marked this pull request as draft September 4, 2026 11:39
@iscekic iscekic removed the human-ready The PR is ready for human review. label Sep 4, 2026
@iscekic
iscekic removed the request for review from eshurakov September 4, 2026 22:40
@iscekic iscekic changed the title fix(mobile): sync app badge with needs-input count fix(mobile): sync app badge with needs-input state Sep 5, 2026
@iscekic iscekic added the human-ready The PR is ready for human review. label Sep 5, 2026
@iscekic
iscekic requested a review from eshurakov September 5, 2026 04:43
@iscekic
iscekic marked this pull request as ready for review September 5, 2026 04:43
@iscekic iscekic removed the human-ready The PR is ready for human review. label Sep 5, 2026
@iscekic
iscekic marked this pull request as draft September 5, 2026 04:49
@iscekic iscekic added the human-ready The PR is ready for human review. label Sep 5, 2026
@iscekic
iscekic marked this pull request as ready for review September 5, 2026 04:54
@iscekic iscekic removed the human-ready The PR is ready for human review. label Sep 5, 2026
@iscekic
iscekic marked this pull request as draft September 5, 2026 11:05
@iscekic iscekic changed the title fix(mobile): sync app badge with needs-input state feat(mobile): sync launcher badge with glanceable needs-input count Sep 5, 2026
@iscekic iscekic added the human-ready The PR is ready for human review. label Sep 5, 2026
@iscekic
iscekic marked this pull request as ready for review September 5, 2026 11:13
@iscekic iscekic removed the human-ready The PR is ready for human review. label Sep 5, 2026
@iscekic
iscekic marked this pull request as draft September 5, 2026 12:10
@iscekic iscekic changed the title feat(mobile): sync launcher badge with glanceable needs-input count feat(mobile): app badge tracks glanceable needs-input count Sep 5, 2026
@iscekic iscekic closed this Sep 6, 2026
@iscekic iscekic reopened this Sep 6, 2026
@iscekic iscekic added the human-ready The PR is ready for human review. label Sep 6, 2026
@iscekic
iscekic marked this pull request as ready for review September 6, 2026 16:04
@iscekic iscekic removed the human-ready The PR is ready for human review. label Sep 6, 2026
@iscekic
iscekic marked this pull request as draft September 6, 2026 16:05
Resolve the glanceable idle rule in favour of main. Main's merged idle-window
design (#5881) keeps a card alive while every agent is idle but never raises
one; this branch had made idle end the card at once. Four sites carried the
older rule:

- packages/app-shared: buildGlanceableSnapshot + isEligibleGlanceableWork
- services/notifications: glanceable-delivery + glanceable-refresh
- apps/mobile: the widget layout, view-props, and the presentation comment

All four now follow main. The branch keeps its own subject, the launcher app
badge fed from the glanceable needs-input count, plus the publisher change
that emits a needs-input change without the coalesce wait.
@iscekic iscekic self-assigned this Sep 6, 2026
@iscekic iscekic added the human-ready The PR is ready for human review. label Sep 6, 2026
@iscekic
iscekic marked this pull request as ready for review September 6, 2026 18:20
@iscekic iscekic removed the human-ready The PR is ready for human review. label Sep 6, 2026
@iscekic
iscekic marked this pull request as draft September 6, 2026 18:20
@iscekic
iscekic marked this pull request as ready for review September 7, 2026 01:10
@iscekic iscekic added the human-ready The PR is ready for human review. label Sep 7, 2026
@iscekic
iscekic merged commit 61a84f6 into main Sep 7, 2026
24 checks passed
@iscekic
iscekic deleted the kwf/deliver-the-work-described-b-fbf8 branch September 7, 2026 12:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

human-ready The PR is ready for human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants